package com.mysteel.util;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.ConnectException;
import java.net.UnknownHostException;

import javax.net.ssl.SSLException;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpStatus;
import org.apache.http.ParseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

/**
 * @author 29  *
 * @date 2017年5月18日 上午9:17:30
 *
 * @Description
 */
public class HttpUtils {
    /**
     * @param uri                      the request address
     * @param json                     the request data that must be a JSON string
     * @param retryCount               the number of times this method has been unsuccessfully
     *                                 executed
     * @param connectTimeout           the timeout in milliseconds until a connection is established
     * @param connectionRequestTimeout the timeout in milliseconds used when requesting a connection
     *                                 from the connection manager
     * @param socketTimeout            the socket timeout in milliseconds, which is the timeout for
     *                                 waiting for data or, put differently, a maximum period
     *                                 inactivity between two consecutive data packets
     * @return null when method parameter is null, "", " "
     * @throws IOException    if HTTP connection can not opened or closed successfully
     * @throws ParseException if response data can not be parsed successfully
     */
    public String retryPostJson(String uri, String json, final int retryCount, int connectTimeout,
                                int connectionRequestTimeout, int socketTimeout) throws IOException, ParseException {
        if (StringUtils.isAnyBlank(uri, json)) {
            return null;
        }

        //  lmba 表达式
        HttpRequestRetryHandler httpRequestRetryHandler = (exception, executionCount, context) -> {
            if (executionCount > retryCount) {
                // Do not retry if over max retry count
                return false;
            }
            if (exception instanceof InterruptedIOException) {
                // An input or output transfer has been terminated
                return false;
            }
            if (exception instanceof UnknownHostException) {
                // Unknown host 修改代码让不识别主机时重试，实际业务当不识别的时候不应该重试，再次为了演示重试过程，执行会显示retryCount次下面的输出
                System.out.println("不识别主机重试");
                return true;
            }
            if (exception instanceof ConnectException) {
                // Connection refused
                return false;
            }
            if (exception instanceof SSLException) {
                // SSL handshake exception
                return false;
            }
            HttpClientContext clientContext = HttpClientContext.adapt(context);
            HttpRequest request = clientContext.getRequest();
            boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
            if (idempotent) {
                // Retry if the request is considered idempotent
                return true;
            }
            return false;
        };

        // 设置重试策略
        CloseableHttpClient client = HttpClients.custom().setRetryHandler(httpRequestRetryHandler).build();
        HttpPost post = new HttpPost(uri);
        // Create request data
        StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
        // Set request body
        post.setEntity(entity);

        RequestConfig config = RequestConfig.custom().setConnectTimeout(connectTimeout)
                .setConnectionRequestTimeout(connectionRequestTimeout).setSocketTimeout(socketTimeout).build();
        post.setConfig(config);
        // Response content
        String responseContent = null;
        CloseableHttpResponse response = null;
        try {
            response = client.execute(post, HttpClientContext.create());
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                responseContent = EntityUtils.toString(response.getEntity(), Consts.UTF_8.name());
            }
        } finally {
            if (ObjectUtils.anyNotNull(response)) {
                response.close();
            }
            if (ObjectUtils.anyNotNull(client)) {
                client.close();
            }
        }
        return responseContent;
    }
}